#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "3d7p_aceMesh.h"
#include "task.h"
#include "ldm_malloc.h"
__thread_local volatile unsigned long get_reply,put_reply;

//_thread_local_fix double A_slave[2][BLKX+2][BLKY+2][BLKZ+2];
__thread_local double A_lhs[BLKX+2][BLKY+2][BLKZ+2];
__thread_local double A_rhs[BLKX+2][BLKY+2][BLKZ+2];
//Record the logical information of this time executed task
__thread_local int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
//Record the logical information of the last time executed task.
__thread_local int last_iter,last_starti,last_startj,last_startk;

extern double A[2][SIZEX][SIZEY][SIZEZ];

#define alpha_d 0.0876
#define beta_d  0.0765
#ifdef _PROFILING
__thread_local int core_id=0;
__thread_local volatile unsigned long start,end,s_comp,e_comp;
__thread_local volatile unsigned long dma_start1,dma_end1, dma_start2,dma_end2,dma_start3,dma_end3,dma_start4,dma_end4;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
extern unsigned long slave_comp_cycle_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif
void stencil_core(targs* args)                                      
{  
   //0. assumptions       
   //data region: [starti-1,endi] [startj-1,endj] [startk-1,endk]  
   //compute region: [starti, endi) [startj,endj) [startk,endk)
   long  array_size; 
   int tsize, bsize, stride;
   unsigned long ic1,ic2;
   int comp_i,comp_j,comp_k;
   int ii,jj,kk;
   s_iter = args->iter;
   dest = (s_iter) % 2 ;
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   s_startk = 1;
   s_endk = args->k_N-1;
//if reuse fetch
   if((s_iter-1) == last_iter && s_starti == last_starti && s_startj == last_startj && s_startk == last_startk)
   {
     int len, bsize, stride;
     core_id = athread_get_id(-1);
#ifdef _PROFILING
     dma_start1=rtc_();
#endif 
     get_reply = 0;
     stride = (SIZEZ-(BLKZ+2))*8;
     len = (BLKY)*(BLKZ+2)*8;
     bsize = (BLKZ+2)*8;
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj][s_startk-1]
                 , &A_lhs[0][1][0]
                 , len, &get_reply
                 , 0, stride, bsize);
     athread_get(PE_MODE,&A[src][s_endi][s_startj][s_startk-1]
                 , &A_lhs[BLKX+1][1][0]
                 , len, &get_reply
                 , 0, stride, bsize);
     for(i = s_starti; i < s_endi; ++i)
     {
       athread_get(PE_MODE,&A[src][i][s_startj-1][s_startk]
                   , &A_lhs[i-s_starti+1][0][1]
                   , (BLKZ)*8,&get_reply,0,0,0);
       athread_get(PE_MODE,&A[src][i][s_endj][s_startk]
                   , &A_lhs[(i-s_starti+1)][BLKY+1][1]
                   , (BLKZ)*8,&get_reply,0,0,0);
     }
/*     for(i = s_starti; i < s_endi; ++i )
       for(j = s_startj; j < s_endj; ++j)
       {
          athread_get(PE_MODE,&A[src][i][j][s_startk-1]
                   , &A_lhs[i-s_starti+1][j-s_startj+1][0]
                   , 8,&get_reply,0,0,0);
          athread_get(PE_MODE,&A[src][i][j][s_endk]
                   , &A_lhs[(i-s_starti+1)][(j-s_startj+1)][(BLKZ+1)]
                   , 8,&get_reply,0,0,0); 
      }*/
        while(get_reply != (2+(BLKX)*2));
      
      //compute
#ifdef _PROFILING
  dma_end1=rtc_();
  s_comp=rtc_();
#endif
     /*  for(ii = 1; ii < BLKX+1; ++ii)
         for(jj = 1; jj < BLKY+1; ++jj)
           for(kk = 1; kk < BLKZ+1; ++kk) 
              A_rhs[ii][jj][kk] = alpha_d * (A_lhs[ii][jj][kk]) + \ 
                     beta_d * (A_lhs[(ii-1)][jj][kk] + A_lhs[(ii+1)][jj][kk] +\
                     A_lhs[ii][jj-1][kk] + A_lhs[ii][(jj+1)][kk]+\
                     A_lhs[ii][jj][(kk-1)] + A_lhs[ii][jj][(kk+1)]);*/
     for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = 1; kk < BLKZ-BLKZ%4; kk+=4)
         { 
            A_rhs[ii][jj][kk] = alpha_d * (A_lhs[ii][jj][kk]) + \
                     beta_d * (A_lhs[(ii-1)][jj][kk] + A_lhs[(ii+1)][jj][kk] +\
                     A_lhs[ii][jj-1][kk] + A_lhs[ii][(jj+1)][kk]+\
                     A_lhs[ii][jj][(kk-1)] + A_lhs[ii][jj][(kk+1)]); 
            A_rhs[ii][jj][kk+1] = alpha_d * (A_lhs[ii][jj][kk+1]) + \
                     beta_d * (A_lhs[(ii-1)][jj][kk+1] + A_lhs[(ii+1)][jj][kk+1] +\
                     A_lhs[ii][jj-1][kk+1] + A_lhs[ii][(jj+1)][kk+1]+\
                     A_lhs[ii][jj][(kk)] + A_lhs[ii][jj][(kk+2)]);
            A_rhs[ii][jj][kk+2] = alpha_d * (A_lhs[ii][jj][kk+2]) + \
                     beta_d * (A_lhs[(ii-1)][jj][kk+2] + A_lhs[(ii+1)][jj][kk+2] +\
                     A_lhs[ii][jj-1][kk+2] + A_lhs[ii][(jj+1)][kk+2]+\
                     A_lhs[ii][jj][(kk+1)] + A_lhs[ii][jj][(kk+3)]);
            A_rhs[ii][jj][kk+3] = alpha_d * (A_lhs[ii][jj][kk+3]) + \
                     beta_d * (A_lhs[(ii-1)][jj][kk+3] + A_lhs[(ii+1)][jj][kk+3] +\
                     A_lhs[ii][jj-1][kk+3] + A_lhs[ii][(jj+1)][kk+3]+\
                     A_lhs[ii][jj][(kk+2)] + A_lhs[ii][jj][(kk+4)]);
         }
     for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = BLKZ-BLKZ%4; kk < BLKZ+1; kk+=1)
         {
            A_rhs[ii][jj][kk] = alpha_d * (A_lhs[ii][jj][kk]) + \
                     beta_d * (A_lhs[(ii-1)][jj][kk] + A_lhs[(ii+1)][jj][kk] +\
                     A_lhs[ii][jj-1][kk] + A_lhs[ii][(jj+1)][kk]+\
                     A_lhs[ii][jj][(kk-1)] + A_lhs[ii][jj][(kk+1)]);
         }
#ifdef _PROFILING
   e_comp=rtc_();
   dma_start2=rtc_();
#endif
//write back
   put_reply=0;
   for(i =  s_starti; i < s_endi; ++i)
     for(j =  s_startj; j < s_endj; ++j)
       athread_put(PE_MODE,&A_rhs[(i-s_starti+1)][(j-s_startj+1)][1]
                     , &A[dest][i][j][s_startk]
                     ,(BLKZ)*8,&put_reply,0,0);
//4.2 synchronize, wait for writing back finish.
   while(put_reply!=(BLKX)*(BLKY));
#ifdef _PROFILING
   dma_end2=rtc_();
#endif
   }else{
//no reuse ldm
//1.1  fetch data
   bsize=(BLKZ+2)*8;
   stride=(SIZEZ-(BLKZ+2))*8;  //stride: from blocktail to blockhead 
   tsize= bsize*(BLKY+2);
#ifdef _PROFILING
   dma_start3=rtc_();
#endif
   get_reply=0;   
   for(i = s_starti-1; i < s_endi+1; i++)
   {   
       athread_get(PE_MODE,&A[src][i][s_startj-1][s_startk-1]
                 , &A_rhs[(i-s_starti+1)][0][0]
                 , tsize, &get_reply
                 , 0, stride, bsize);
   }
   while(get_reply!=(BLKX+2));
#ifdef _PROFILING
  dma_end3=rtc_();
  start=rtc_();
#endif
   //computation
/*    for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = 1; kk < BLKZ+1; ++kk)
            A_lhs[ii][jj][kk] = alpha_d * (A_rhs[ii][jj][kk]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk] + A_rhs[(ii+1)][jj][kk] +\
                     A_rhs[ii][jj-1][kk] + A_rhs[ii][(jj+1)][kk]+\
                     A_rhs[ii][jj][(kk-1)] + A_rhs[ii][jj][(kk+1)]); */
    for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = 1; kk < BLKZ-BLKZ%4; kk+=4)
         {
            A_lhs[ii][jj][kk] = alpha_d * (A_rhs[ii][jj][kk]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk] + A_rhs[(ii+1)][jj][kk] +\
                     A_rhs[ii][jj-1][kk] + A_rhs[ii][(jj+1)][kk]+\
                     A_rhs[ii][jj][(kk-1)] + A_rhs[ii][jj][(kk+1)]);
            A_lhs[ii][jj][kk+1] = alpha_d * (A_rhs[ii][jj][kk+1]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk+1] + A_rhs[(ii+1)][jj][kk+1] +\
                     A_rhs[ii][jj-1][kk+1] + A_rhs[ii][(jj+1)][kk+1]+\
                     A_rhs[ii][jj][(kk)] + A_rhs[ii][jj][(kk+2)]);
            A_lhs[ii][jj][kk+2] = alpha_d * (A_rhs[ii][jj][kk+2]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk+2] + A_rhs[(ii+1)][jj][kk+2] +\
                     A_rhs[ii][jj-1][kk+2] + A_rhs[ii][(jj+1)][kk+2]+\
                     A_rhs[ii][jj][(kk+1)] + A_rhs[ii][jj][(kk+3)]);
            A_lhs[ii][jj][kk+3] = alpha_d * (A_rhs[ii][jj][kk+3]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk+3] + A_rhs[(ii+1)][jj][kk+3] +\
                     A_rhs[ii][jj-1][kk+3] + A_rhs[ii][(jj+1)][kk+3]+\
                     A_rhs[ii][jj][(kk+2)] + A_rhs[ii][jj][(kk+4)]);
         }
     for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = BLKZ-BLKZ%4; kk < BLKZ+1; kk+=1)
         {
            A_lhs[ii][jj][kk] = alpha_d * (A_rhs[ii][jj][kk]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk] + A_rhs[(ii+1)][jj][kk] +\
                     A_rhs[ii][jj-1][kk] + A_rhs[ii][(jj+1)][kk]+\
                     A_rhs[ii][jj][(kk-1)] + A_rhs[ii][jj][(kk+1)]);
         }
   
#ifdef _PROFILING
  end=rtc_();
  slave_comp_count[core_id]+=((end-start)+(e_comp-s_comp));
#endif
//3.record information of last task
   last_iter = s_iter;
   last_starti = s_starti;
   last_startj = s_startj;
   last_startk = s_startk;
//4.1 write back
#ifdef _PROFILING
  dma_start4=rtc_();
#endif
   put_reply=0;
   for(i =  s_starti; i < s_endi; ++i)
     for(j =  s_startj; j < s_endj; ++j)  
       athread_put(PE_MODE,&A_lhs[(i-s_starti+1)][(j-s_startj+1)][1]
                     , &A[dest][i][j][s_startk]
                     ,(BLKZ)*8,&put_reply,0,0);
//4.2 synchronize, wait for writing back finish.
   while(put_reply!=(BLKX)*(BLKY));
#ifdef _PROFILING
  dma_end4=rtc_();
  slave_dma_count[core_id]+=((dma_end1-dma_start1)+(dma_end2-dma_start2)+(dma_end3-dma_start3)+(dma_end4-dma_start4));
#endif
//5.free A_slave_array AND return to scheduler loop.
 }
#ifdef __MAIN_MEM
//access main memory code
      int i,j,k;
      int  dest = (args->iter) % 2 ; 
      int  src = 1 - dest;
      for(i =  args->starti; i <  args->endi; i++)
        for(j =  args->startj; j <  args->endj; j++)
          for(k = args->startk; k <  args->endk; k++) 
          {   
             A[dest][i][j][k] = alpha_d * (A[(src)][(i)][(j)][(k)]) +\
                        beta_d * (A[src][i-1][j][k] + A[src][i+1][j][k] +\
                        A[src][i][j-1][k] + A[src][i][j+1][k] +\
                        A[src][i][j][k-1] + A[src][i][j][k+1]);
          }
#endif
}
